#include <sdk.h>
#include "ThreadSearch.h"
#include "ThreadSearchLoggerTree.h"
#include "ThreadSearchEvent.h"
#include "ThreadSearchView.h"
#include "ThreadSearchControlIds.h"

ThreadSearchLoggerTree::ThreadSearchLoggerTree( ThreadSearchView& threadSearchView,
    ThreadSearch& threadSearchPlugin,
    InsertIndexManager::eFileSorting fileSorting,
    wxPanel* pParent,
    long id )
  : ThreadSearchLoggerBase( threadSearchView, threadSearchPlugin, fileSorting )
  , m_pTreeLog( NULL )
  , m_FirstItemProcessed( false ) {
  m_pTreeLog = new wxTreeCtrl( pParent, id, wxDefaultPosition, wxSize( 1, 1 ), wxTR_HAS_BUTTONS | wxTR_LINES_AT_ROOT | wxTR_FULL_ROW_HIGHLIGHT | wxTR_HIDE_ROOT | wxTR_DEFAULT_STYLE | wxSUNKEN_BORDER );
  m_pTreeLog->SetMinSize( wxSize( 100, 100 ) );
  m_FilesParentId = m_pTreeLog->AddRoot( wxEmptyString );
  ConnectEvents( pParent );
}

ThreadSearchLoggerTree::~ThreadSearchLoggerTree() {
  wxWindow* pParent = m_pTreeLog->GetParent();
  if( pParent != NULL ) {
    DisconnectEvents( pParent );
  }
  m_pTreeLog->Destroy();
  m_pTreeLog = NULL;
}

wxWindow* ThreadSearchLoggerTree::GetWindow() {
  return m_pTreeLog;
}

void ThreadSearchLoggerTree::SetFocus() {
  m_pTreeLog->SetFocus();
}

void ThreadSearchLoggerTree::ConnectEvents( wxEvtHandler* pEvtHandler ) {
  int id = m_pTreeLog->GetId();
  pEvtHandler->Connect( id, wxEVT_COMMAND_TREE_SEL_CHANGED,
                        ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
                        &ThreadSearchLoggerTree::OnLoggerTreeClick, NULL, this );
  pEvtHandler->Connect( id, wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
                        ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
                        &ThreadSearchLoggerTree::OnLoggerTreeDoubleClick, NULL, this );
  pEvtHandler->Connect( id, wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,
                        ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
                        &ThreadSearchLoggerTree::OnLoggerTreeContextualMenu, NULL, this );
  pEvtHandler->Connect( controlIDs.Get( ControlIDs::idMenuCtxDeleteItem ), wxEVT_COMMAND_MENU_SELECTED,
                        wxCommandEventHandler( ThreadSearchLoggerTree::OnDeleteTreeItem ), NULL, this );
  pEvtHandler->Connect( controlIDs.Get( ControlIDs::idMenuCtxDeleteAllItems ), wxEVT_COMMAND_MENU_SELECTED,
                        wxCommandEventHandler( ThreadSearchLoggerTree::OnDeleteAllTreeItems ), NULL, this );
}

void ThreadSearchLoggerTree::DisconnectEvents( wxEvtHandler* pEvtHandler ) {
  int id = m_pTreeLog->GetId();
  pEvtHandler->Disconnect( id, wxEVT_COMMAND_TREE_SEL_CHANGED,
                           ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
                           &ThreadSearchLoggerTree::OnLoggerTreeClick, NULL, this );
  pEvtHandler->Disconnect( id, wxEVT_COMMAND_TREE_ITEM_ACTIVATED,
                           ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
                           &ThreadSearchLoggerTree::OnLoggerTreeDoubleClick, NULL, this );
  #if defined ( __WXGTK__ )
  pEvtHandler->Disconnect( id, wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK,
  #else
  pEvtHandler->Disconnect( id, wxEVT_COMMAND_TREE_ITEM_MENU,
  #endif
                           ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
                           &ThreadSearchLoggerTree::OnLoggerTreeContextualMenu, NULL, this );
  pEvtHandler->Disconnect( controlIDs.Get( ControlIDs::idMenuCtxDeleteItem ), wxEVT_COMMAND_MENU_SELECTED,
                           wxCommandEventHandler( ThreadSearchLoggerTree::OnDeleteTreeItem ), NULL, this );
  pEvtHandler->Disconnect( controlIDs.Get( ControlIDs::idMenuCtxDeleteAllItems ), wxEVT_COMMAND_MENU_SELECTED,
                           wxCommandEventHandler( ThreadSearchLoggerTree::OnDeleteAllTreeItems ), NULL, this );
}

void ThreadSearchLoggerTree::OnLoggerTreeClick( wxTreeEvent& event ) {
  if( m_pTreeLog->GetCount() == 0 ) {
    return;
  }
  wxTreeItemId itemId = event.GetItem();
  if( itemId.IsOk() && hasResultLineForTreeItem( itemId ) ) {
    wxString filepath( wxEmptyString );
    long line;
    if( GetFileLineFromTreeEvent( event, filepath, line ) == false ) {
      msging( _T( "Failed to retrieve file path and line number" ), _T( "错误" ), wxICON_ERROR );
      return;
    }
    m_ThreadSearchView.OnLoggerClick( filepath, line );
  }
  event.Skip();
}

void ThreadSearchLoggerTree::OnLoggerTreeDoubleClick( wxTreeEvent& event ) {
  wxTreeItemId itemId = event.GetItem();
  if( itemId.IsOk() && hasResultLineForTreeItem( itemId ) ) {
    wxString filepath( wxEmptyString );
    long line;
    if( GetFileLineFromTreeEvent( event, filepath, line ) == false ) {
      msging( _T( "Failed to retrieve file path and line number" ), _T( "错误" ), wxICON_ERROR );
      return;
    }
    m_ThreadSearchView.OnLoggerDoubleClick( filepath, line );
  }
  event.Skip();
}

void ThreadSearchLoggerTree::OnLoggerTreeContextualMenu( wxTreeEvent& event ) {
  wxPoint clientPoint = event.GetPoint();
  m_ToDeleteItemId = event.GetItem();
  ShowMenu( clientPoint );
}

bool ThreadSearchLoggerTree::hasResultLineForTreeItem( wxTreeItemId treeItemId ) {
  return ( m_pTreeLog->GetItemText( treeItemId ).StartsWith( _T( "=>" ) ) == false ) || ( m_pTreeLog->ItemHasChildren( treeItemId ) );
}

bool ThreadSearchLoggerTree::GetFileLineFromTreeEvent( wxTreeEvent& event, wxString& filepath, long &line ) {
  wxTreeItemId lineItemId;
  wxTreeItemId fileItemId;
  wxTreeItemId tmpItemId;
  wxTreeItemId eventItemId = event.GetItem();
  bool success( false );
  filepath = wxEmptyString;
  line = 0;
  do {
    wxTreeItemIdValue cookie;
    tmpItemId = eventItemId;
    do {
      lineItemId = tmpItemId;
      tmpItemId = m_pTreeLog->GetFirstChild( lineItemId, cookie );
    } while( tmpItemId.IsOk() == true );
    fileItemId = m_pTreeLog->GetItemParent( lineItemId );
    const wxString& lineText = m_pTreeLog->GetItemText( lineItemId );
    int columnPos = lineText.Find( ':' );
    if( columnPos == wxNOT_FOUND ) {
      break;
    }
    if( lineText.Left( columnPos ).ToLong( &line ) == false ) {
      break;
    }
    const wxString& fileText = m_pTreeLog->GetItemText( fileItemId );
    int fileLength = fileText.Find( _T( " (" ) );
    if( fileLength == wxNOT_FOUND ) {
      break;
    }
    int dirBegin = fileLength + 2;
    int dirLength  = fileText.Length() - dirBegin - 1;
    if( dirLength <= 0 ) {
      break;
    }
    wxFileName filename( fileText.Mid( dirBegin, dirLength ), fileText.Left( fileLength ) );
    filepath = filename.GetFullPath();
    success = true;
  } while( false );
  return success;
}

void ThreadSearchLoggerTree::OnThreadSearchEvent( const ThreadSearchEvent& event ) {
  const wxArrayString& words  = event.GetLineTextArray();
  const wxFileName&  filename( event.GetString() );
  bool       setFocus( false );
  wxTreeItemId   fileItemId;
  long       index  = m_IndexManager.GetInsertionIndex( filename.GetFullPath() );
  long       nb_items = m_pTreeLog->GetChildrenCount( m_FilesParentId, false );
  wxASSERT( index != wxNOT_FOUND );
  wxASSERT( ( words.GetCount() % 2 ) == 0 );
  m_pTreeLog->Freeze();
  wxTreeItemId lineItemId;
  if( index == nb_items ) {
    fileItemId = m_pTreeLog->AppendItem( m_FilesParentId, F( _T( "%s (%s)" ),
                                         filename.GetFullName().c_str(),
                                         filename.GetPath().c_str() ) );
  } else {
    fileItemId = m_pTreeLog->InsertItem( m_FilesParentId, index, F( _T( "%s (%s)" ),
                                         filename.GetFullName().c_str(),
                                         filename.GetPath().c_str() ) );
  }
  for( size_t i = 0; i < words.GetCount(); i += 2 ) {
    lineItemId = m_pTreeLog->AppendItem( fileItemId, F( _T( "%s: %s" ),
                                         words[i].c_str(), words[i + 1].c_str() ) );
    if( ( m_FirstItemProcessed == false )        &&
        ( m_pTreeLog->GetChildrenCount( fileItemId, false ) == 1 ) &&
        ( m_pTreeLog->GetChildrenCount( m_FilesParentId, false ) == 1 ) ) {
      m_pTreeLog->Expand( fileItemId );
      m_pTreeLog->SelectItem( lineItemId );
      m_FirstItemProcessed = true;
      setFocus = true;
    }
  }
  m_pTreeLog->Thaw();
  if( setFocus == true ) {
    m_pTreeLog->SetFocus();
  }
}


void ThreadSearchLoggerTree::Clear() {
  wxWindow* pParent = m_pTreeLog->GetParent();
  if( pParent == NULL ) {
    return;
  }
  DisconnectEvents( pParent );
  m_pTreeLog->DeleteChildren( m_pTreeLog->GetRootItem() );
  m_FirstItemProcessed = false;
  m_IndexManager.Reset();
  ConnectEvents( pParent );
}

void ThreadSearchLoggerTree::OnSearchBegin( const ThreadSearchFindData& findData ) {
  if( m_ThreadSearchPlugin.GetDeletePreviousResults() ) {
    Clear();
    m_FilesParentId = m_pTreeLog->GetRootItem();
  } else {
    m_IndexManager.Reset();
    m_FirstItemProcessed = false;
    m_FilesParentId = m_pTreeLog->AppendItem( m_pTreeLog->GetRootItem(),
                      F( _T( "=> %s" ), findData.GetFindText().c_str() ) );
  }
}

void ThreadSearchLoggerTree::OnDeleteTreeItem( M_Code_unused wxCommandEvent& event ) {
  if( m_ToDeleteItemId.IsOk() ) {
    wxTreeItemId rootId = m_pTreeLog->GetRootItem();
    wxTreeItemId parentId = m_pTreeLog->GetItemParent( m_ToDeleteItemId );
    while( ( parentId != rootId ) && ( m_pTreeLog->GetChildrenCount( parentId, false ) == 1 ) ) {
      m_ToDeleteItemId = parentId;
      parentId = m_pTreeLog->GetItemParent( m_ToDeleteItemId );
    }
    DeleteTreeItem( m_ToDeleteItemId );
  }
}

void ThreadSearchLoggerTree::DeleteTreeItem( wxTreeItemId id ) {
  if( id.IsOk() ) {
    wxWindow* pParent = m_pTreeLog->GetParent();
    if( pParent == NULL ) {
      return;
    }
    DisconnectEvents( pParent );
    m_pTreeLog->Delete( id );
    ConnectEvents( pParent );
    wxTreeItemId selectedItemId = m_pTreeLog->GetSelection();
    if( selectedItemId.IsOk() ) {
      m_pTreeLog->SelectItem( selectedItemId, true );
    }
  }
}

void ThreadSearchLoggerTree::OnDeleteAllTreeItems( wxCommandEvent& ) {
  if( m_ToDeleteItemId.IsOk() ) {
    DeleteTreeItems();
  }
}

void ThreadSearchLoggerTree::DeleteTreeItems() {
  Clear();
}
